package com.boot.persistence;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.boot.cache.AbstractCache;
import com.boot.persistence.condition.QueryCondition;
import com.boot.persistence.mapper.IMapper;
import com.boot.persistence.model.BaseModel;
import com.boot.persistence.service.ServiceImpl;
import org.springframework.lang.Nullable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @description 缓存层抽象类
 * @date 2023/2/5 16:13
 **/
public abstract class CacheServiceImpl<Id extends Serializable, T extends BaseModel<Id>, Mapper extends IMapper<T>> extends ServiceImpl<Id, T, Mapper> {

    private final @Nullable AbstractCache<Id, T> modelCache;
    /**
     * @description pageCache 只适用于 只读表
     * @date 2023/1/23 21:37
     **/
    private final @Nullable AbstractCache<QueryCondition<Id, T>, IPage<T>> pageCache;

    protected CacheServiceImpl(Mapper mapper) {
        this(mapper, null, null);
    }

    public CacheServiceImpl(Mapper mapper, AbstractCache<Id, T> modelCache) {
        this(mapper, modelCache, null);
    }

    public CacheServiceImpl(Mapper mapper, AbstractCache<Id, T> modelCache, AbstractCache<QueryCondition<Id, T>, IPage<T>> pageCache) {
        super(mapper);
        this.modelCache = modelCache;
        this.pageCache = pageCache;
    }

    public AbstractCache<Id, T> getModelCache() {
        return modelCache;
    }

    public AbstractCache<QueryCondition<Id, T>, IPage<T>> getPageCache() {
        return pageCache;
    }

    @Override
    public T getById(Id id) {
        if (modelCache == null) {
            return super.getById(id);
        }
        return modelCache.exec(id, super::getById);
    }

    @Override
    public IPage<T> select(QueryCondition<Id, T> condition) {
        if (pageCache == null) {
            return super.select(condition);
        }
        return pageCache.exec(condition, super::select);
    }

    @Override
    public IPage<T> selectLike(QueryCondition<Id, T> condition) {
        if (pageCache == null) {
            return super.selectLike(condition);
        }
        return pageCache.exec(condition, super::selectLike);
    }

    @Override
    public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
        T model = super.getOne(queryWrapper, throwEx);
        if (modelCache != null) {
            modelCache.setCache(model.pk(), model);
        }
        return model;
    }

    @Override
    public List<T> listByIds(Collection<? extends Id> idList) {
        List<T> modelList = super.listByIds(idList);
        if (modelCache != null && !CollectionUtils.isEmpty(modelList)) {
            modelList.forEach(model -> modelCache.setCache(model.pk(), model));
        }
        return modelList;
    }

    @Override
    public List<T> list(Wrapper<T> queryWrapper) {
        List<T> modelList = super.list(queryWrapper);
        if (modelCache != null && !CollectionUtils.isEmpty(modelList)) {
            modelList.forEach(model -> modelCache.setCache(model.pk(), model));
        }
        return modelList;
    }

    @Override
    public <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
        E pageInfo = super.page(page, queryWrapper);
        if (modelCache != null && !CollectionUtils.isEmpty(pageInfo.getRecords())) {
            pageInfo.getRecords().forEach(model -> modelCache.setCache(model.pk(), model));
        }
        return pageInfo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateById(T model) {
        boolean ans = super.updateById(model);
        if (modelCache != null && ans) {
            modelCache.deleteCache(model.pk());
        }
        return ans;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeById(Id id) {
        boolean ans = super.removeById(id);
        if (modelCache != null && ans) {
            modelCache.deleteCache(id);
        }
        return ans;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeByIds(Collection<? extends Id> idList) {
        boolean ans = super.removeByIds(idList);
        if (modelCache != null && ans) {
            modelCache.deleteCaches(idList);
        }
        return ans;
    }
}
